﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Mail;
using System.ServiceModel;
using System.Text.RegularExpressions;
using System.Threading;
using Microsoft.Crm.Sdk.Messages;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Messages;
using Microsoft.Xrm.Sdk.Metadata;

namespace QueueManager.Helper
{
  public struct NameStruct
  {
    public string FirstName;
    public string LastName;
  }
  public enum PartType
  {
    Fullname,
    FirstName,
    LastName,
    CompanyName,
    Url
  }

  // CRMHelper
  //
  // Various helper functions for CRM Queue Manager
  public class CrmHelper
  {
    private readonly IOrganizationService _service;
    private readonly ServiceContext _svcContext;

    public CrmHelper(IOrganizationService service)
    {
      _service = service;
      _svcContext = new ServiceContext(service);
    }

    public SystemUser GetSystemUser(Guid userId)
    {
      try
      {
        //Query for the queue
        return _svcContext.SystemUserSet.FirstOrDefault(s => s.Id == userId);
      }
      // Catch any service fault exceptions that Microsoft Dynamics CRM throws.
      catch (FaultException<Microsoft.Xrm.Sdk.OrganizationServiceFault>)
      {
        // You can handle an exception here or pass it back to the calling method.
        throw;
      }
    }

    public Queue GetQueue(Guid queueId)
    {
      try
      {
        var existingQueue = (from queue in _svcContext.QueueSet
                             where queue.QueueId == queueId
                             select queue).FirstOrDefault();

        //Query for the queue
        return existingQueue;
      }
      // Catch any service fault exceptions that Microsoft Dynamics CRM throws.
      catch (FaultException<Microsoft.Xrm.Sdk.OrganizationServiceFault>)
      {
        // You can handle an exception here or pass it back to the calling method.
        throw;
      }

    }
    public List<Queue> GetQueues(EntityReference ownerId, int queueTypeCode)
    {
      try
      {
        //Find all queues of a certain type
        return _svcContext.QueueSet.Where(q => q.OwnerId.Id == ownerId.Id).ToList();
        //return _svcContext.QueueSet.Where(q => q.OwnerId.Id == ownerId.Id && q.QueueTypeCode.Value == queueTypeCode).ToList();
      }
      // Catch any service fault exceptions that Microsoft Dynamics CRM throws.
      catch (FaultException<Microsoft.Xrm.Sdk.OrganizationServiceFault>)
      {
        // You can handle an exception here or pass it back to the calling method.
        throw;
      }
    }

    public List<QueueItem> GetQueueItems(Queue queue, bool removeRepliesFromQueue)
    {
      try
      {
        //Query the queue for queue items and retrieve the columns neccessary
        var items = new List<QueueItem>();

        //Add email as a linked entity and add a condition to only process e-mail with nothing in the regarding field.
        //That should be all new e-mail. 
        if (!removeRepliesFromQueue)
        {
          items = (from q in _svcContext.QueueItemSet
                   join e in _svcContext.EmailSet on q.ObjectId.Id equals e.ActivityId.Value
                   where (q.QueueId.Id == queue.Id && q.ObjectTypeCode.Value == 4202)
                   where (e.RegardingObjectId == null)
                   select q).ToList();
        }
        else
        {
          items = (from q in _svcContext.QueueItemSet
                   where (q.QueueId.Id == queue.Id && q.ObjectTypeCode.Value == 4202)
                   select q).ToList();
        }

        LogHelper.Output("Querying queue '{0}'...", queue.Name);
        LogHelper.Output("Found {0} e-mail on queue.", items.Count.ToString());

        return items;
      }
      // Catch any service fault exceptions that Microsoft Dynamics CRM throws.
      catch (FaultException<Microsoft.Xrm.Sdk.OrganizationServiceFault>)
      {
        // You can handle an exception here or pass it back to the calling method.
        throw;
      }

    }
    public List<QueueItem> GetQueueItems(Guid queueId, string queueName, DateTime before)
    {
      try
      {
        //Query the queue for queue items and retrieve the columns neccessary

        //Add email as a linked entity and add a condition to only process e-mail with nothing in the regarding field.
        //That should be all new e-mail. 
        var queueItems = _svcContext.QueueItemSet.Where(qi => qi.QueueId.Id == queueId && qi.ObjectTypeCode.Value == 4202 && qi.EnteredOn.Value < before).ToList();

        LogHelper.Output("Querying queue '{0}'...", queueName);
        LogHelper.Output("Found {0} e-mail on queue.", queueItems.Count.ToString());

        return queueItems;
      }
      // Catch any service fault exceptions that Microsoft Dynamics CRM throws.
      catch (FaultException<Microsoft.Xrm.Sdk.OrganizationServiceFault>)
      {
        // You can handle an exception here or pass it back to the calling method.
        throw;
      }

    }
    public void DeleteQueueItem(QueueItem queueItem)
    {
      _service.Delete(QueueItem.EntityLogicalName, queueItem.Id);
    }

    public Email GetEmail(Guid emailId, string title)
    {
      try
      {
        LogHelper.Output("Retrieving e-mail '{0}'...", title);
        var a = _svcContext.EmailSet.FirstOrDefault(e => e.Id == emailId);

        return _svcContext.EmailSet.FirstOrDefault(e => e.Id == emailId);

      }
      catch (Exception ex)
      {
        LogHelper.Log("Error retrieving email from queue item.", ex, LogHelper.LogType.Exception);
        return null;
      }
    }
    public Email GetEmail(string trackingtoken)
    {
      try
      {
        return _svcContext.EmailSet.FirstOrDefault(e => e.TrackingToken == trackingtoken);
      }
      catch (Exception ex)
      {
        LogHelper.Log("Error retrieving email from queue item.", ex, LogHelper.LogType.Exception);
        return null;
      }
    }

    public List<Contact> GetContacts(string emailAddress)
    {
      try
      {
        //Query for the contact
        return _svcContext.ContactSet.Where(c => c.EMailAddress1 == emailAddress || c.EMailAddress2 == emailAddress || c.EMailAddress3 == emailAddress).ToList();
      }
      // Catch any service fault exceptions that Microsoft Dynamics CRM throws.
      catch (FaultException<OrganizationServiceFault> ex)
      {
        // You can handle an exception here or pass it back to the calling method.
        LogHelper.Log("Error retrieving contacts.", ex, LogHelper.LogType.Exception);
        throw;
      }
    }
    public List<Contact> GetContactsByDomain(string domain, int numberOfRowsToReturn)
    {
      try
      {
        //Query for the contact
        return _svcContext.ContactSet.Where(c => c.EMailAddress1.EndsWith(domain)).Take(numberOfRowsToReturn).ToList();
      }
      // Catch any service fault exceptions that Microsoft Dynamics CRM throws.
      catch (FaultException<OrganizationServiceFault> ex)
      {
        // You can handle an exception here or pass it back to the calling method.
        LogHelper.Log("Error retrieving contacts.", ex, LogHelper.LogType.Exception);
        throw;
      }
    }
    public Contact GetContact(Guid contactId)
    {
      try
      {
        //Query for the contact
        return _svcContext.ContactSet.FirstOrDefault(c => c.Id == contactId);
      }
      // Catch any service fault exceptions that Microsoft Dynamics CRM throws.
      catch (FaultException<OrganizationServiceFault> ex)
      {
        // You can handle an exception here or pass it back to the calling method.
        LogHelper.Log("Error retrieving contacts.", ex, LogHelper.LogType.Exception);
        throw;
      }
    }
    public Contact GetContactByEmail(string primaryEmailAddress)
    {
      try
      {
        //Query for the contact
        return _svcContext.ContactSet.FirstOrDefault(c => c.EMailAddress1 == primaryEmailAddress);
      }
      // Catch any service fault exceptions that Microsoft Dynamics CRM throws.
      catch (FaultException<OrganizationServiceFault> ex)
      {
        // You can handle an exception here or pass it back to the calling method.
        LogHelper.Log("Error retrieving contacts.", ex, LogHelper.LogType.Exception);
        throw;
      }
    }
    public Contact GetContactByFullName(string fullName)
    {
      try
      {
        //Query for the contact
        return _svcContext.ContactSet.FirstOrDefault(c => c.FullName == fullName);
      }
      // Catch any service fault exceptions that Microsoft Dynamics CRM throws.
      catch (FaultException<OrganizationServiceFault> ex)
      {
        // You can handle an exception here or pass it back to the calling method.
        LogHelper.Log("Error retrieving contacts.", ex, LogHelper.LogType.Exception);
        throw;
      }
    }

    public Guid Create(Entity entity)
    {
      try
      {
        //Create the entity
        return _service.Create(entity);
      }
      // Catch any service fault exceptions that Microsoft Dynamics CRM throws.
      catch (FaultException<OrganizationServiceFault> ex)
      {
        // You can handle an exception here or pass it back to the calling method.
        LogHelper.Log("Error creating entity.", ex, LogHelper.LogType.Exception);
        throw;
      }
    }
    public void Update(Entity entity)
    {
      try
      {
        //Create the entity
        _service.Update(entity);
      }
      // Catch any service fault exceptions that Microsoft Dynamics CRM throws.
      catch (FaultException<OrganizationServiceFault> ex)
      {
        // You can handle an exception here or pass it back to the calling method.
        LogHelper.Log("Error creating entity.", ex, LogHelper.LogType.Exception);
        throw;
      }
    }
    public void Delete(Guid objectId)
    {
      try
      {
        _service.Delete(QueueItem.EntityLogicalName, objectId);
      }
      // Catch any service fault exceptions that Microsoft Dynamics CRM throws.
      catch (FaultException<Microsoft.Xrm.Sdk.OrganizationServiceFault>)
      {
        // You can handle an exception here or pass it back to the calling method.
        throw;
      }
    }

    public Incident GetCase(string ticketNumber)
    {
      try
      {
        //Query for the queue
        return _svcContext.IncidentSet.FirstOrDefault(i => i.TicketNumber == ticketNumber);
      }
      // Catch any service fault exceptions that Microsoft Dynamics CRM throws.
      catch (FaultException<Microsoft.Xrm.Sdk.OrganizationServiceFault>)
      {
        // You can handle an exception here or pass it back to the calling method.
        throw;
      }

    }
    public Incident GetCase(Guid incidentId)
    {
      try
      {
        //Query for the queue
        return _svcContext.IncidentSet.FirstOrDefault(i => i.Id == incidentId);
      }
      // Catch any service fault exceptions that Microsoft Dynamics CRM throws.
      catch (FaultException<Microsoft.Xrm.Sdk.OrganizationServiceFault>)
      {
        // You can handle an exception here or pass it back to the calling method.
        throw;
      }

    }
    public Guid CreateCase(Incident incident)
    {
      try
      {
        //Create the incident
        return _service.Create(incident);
      }
      // Catch any service fault exceptions that Microsoft Dynamics CRM throws.
      catch (FaultException<OrganizationServiceFault> ex)
      {
        // You can handle an exception here or pass it back to the calling method.
        LogHelper.Log("Error setting tracking token on email.", ex, LogHelper.LogType.Exception);
        throw;
      }
    }

    public Organization GetOrgInfo()
    {
      try
      {
        var objWhoAmIReq = new WhoAmIRequest();
        var objWhoAmIRes = (WhoAmIResponse)_service.Execute(objWhoAmIReq);

        //Query for the queue
        return _svcContext.OrganizationSet.FirstOrDefault(o => o.Id == objWhoAmIRes.OrganizationId);
      }
      // Catch any service fault exceptions that Microsoft Dynamics CRM throws.
      catch (FaultException<Microsoft.Xrm.Sdk.OrganizationServiceFault>)
      {
        // You can handle an exception here or pass it back to the calling method.
        throw;
      }
    }

    public AttributeMetadata GetAttribute(string logicalName, string name)
    {
      try
      {
        var entityRequest = new RetrieveEntityRequest
           {
             EntityFilters = EntityFilters.All,
             LogicalName = logicalName
           };

        var entityResponse = (RetrieveEntityResponse)_service.Execute(entityRequest);
        return entityResponse.EntityMetadata.Attributes.FirstOrDefault(attribute => attribute.LogicalName.ToLower() == name.ToLower());
      }
      // Catch any service fault exceptions that Microsoft Dynamics CRM throws.
      catch (FaultException<OrganizationServiceFault> ex)
      {
        // You can handle an exception here or pass it back to the calling method.
        LogHelper.Log("Error retrieving contacts.", ex, LogHelper.LogType.Exception);
        throw;
      }
    }

    public void AddTrackingTokenToEmail(Email email, string token)
    {
      try
      {
        email.TrackingToken = token;
        _service.Update(email);
      }
      // Catch any service fault exceptions that Microsoft Dynamics CRM throws.
      catch (FaultException<OrganizationServiceFault> ex)
      {
        // You can handle an exception here or pass it back to the calling method.
        LogHelper.Log("Error setting tracking token on email.", ex, LogHelper.LogType.Exception);
        throw;
      }
    }
    public void AssociateEmailWithCase(Email email, Incident incident)
    {
      try
      {
        // Create a collection of the entities that will be associated to the case.
        var relatedEntities = new EntityReferenceCollection();
        relatedEntities.Add(new EntityReference(Incident.EntityLogicalName, incident.Id));

        // Create an object that defines the relationship between the email and the case.
        var relationship = new Relationship("Incident_Emails");

        //Associate the email with the case.
        _service.Associate(Email.EntityLogicalName, email.Id, relationship, relatedEntities);
      }
      // Catch any service fault exceptions that Microsoft Dynamics CRM throws.
      catch (FaultException<OrganizationServiceFault> ex)
      {
        // You can handle an exception here or pass it back to the calling method.
        LogHelper.Log("Error associating the email with the case.", ex, LogHelper.LogType.Exception);
        throw;
      }
    }

    /// <summary>
    /// Resolve a parent account based on the e-mail address
    /// </summary>
    /// <param name="svcContext"> </param>
    /// <param name="contact">The contact</param>
    /// <param name="address">The e-mail address</param>
    /// <param name="resolveMailFrom">The from address when sending resolve e-mail.</param>
    /// <param name="resolveMailTo">The to address to send e-mail to.</param>
    public Contact ResolveParentAccount(Contact contact, string address, string resolveMailTo, string resolveMailFrom)
    {
      var domain = "";
      var accountId = "";

      //Get the domain part 
      if (address.Split('@').Length == 2)
      {
        domain = "%@" + address.Split('@')[1];
      }
      else
      {
        return contact;
      }

      //Only sample 10 first contacts
      List<Contact> contacts = null;
      try
      {
        contacts = GetContactsByDomain(domain, 10);
      }
      catch (Exception ex)
      {
        LogHelper.Log("Error retrieving contacts when resolving parent account.", ex, LogHelper.LogType.Exception);
        return contact;
      }

      //Loop through and figure out the parent account for contacts with same e-mail domain.
      //Only do this if all contacts return same contact.
      foreach (var objContact in contacts)
      {
        if (objContact.ParentCustomerId != null)
        {
          //Right, let's see if we have already found a customer
          if (accountId == "")
          {
            accountId = objContact.ParentCustomerId.Id.ToString();
          }
          else
          {
            //If the current customer differs then send mail and return the contact unchanged
            if (accountId != objContact.ParentCustomerId.Id.ToString())
            {
              if (!string.IsNullOrEmpty(resolveMailTo))
              {
                //Send an e-mail
                try
                {
                  SendNetMail("CRM Queue Manager parent account matching (failed)", string.Format("More than one account was found for contact '{0}'. Accounts found: {1}{2}{3}{4}", contact.EMailAddress1, Environment.NewLine + Environment.NewLine, accountId.ToString(), Environment.NewLine, objContact.ParentCustomerId.Name), resolveMailTo, "", resolveMailFrom, false);
                }
                catch (Exception ex)
                {
                  //Catch and move on
                  LogHelper.Log("Error sending resolve mail (1).", ex, LogHelper.LogType.Exception);
                }

                //Return the contact unchanged
                return contact;
              }
            }
          }

        }
      }

      //Let's see if we have found a customer and update the parentcustomerid if we have.
      if (accountId != "")
      {
        contact.ParentCustomerId = new EntityReference(Account.EntityLogicalName, new Guid(accountId));

        if (!string.IsNullOrEmpty(resolveMailTo))
        {
          //Send an e-mail
          try
          {
            SendNetMail("CRM Queue Manager parent account matching (success)", string.Format("Automatically matched parent account for contact '{0} {1} ({2})'. Accounts is set to '{3}'.", contact.FirstName, contact.LastName, contact.EMailAddress1, accountId), resolveMailTo, "", resolveMailFrom, false);
          }
          catch (Exception ex)
          {
            //Catch and move on
            LogHelper.Log("Error sending resolve mail (2).", ex, LogHelper.LogType.Exception);
          }
        }
      }

      return contact;

    }

    /// <summary>
    /// Return a resolved part of an e-mail address.
    /// </summary>
    /// <param name="address">E-Mail to resolve</param>
    /// <param name="typeToReturn">The part to resolve</param>
    public string GetPartFromEMail(string address, PartType typeToReturn)
    {

      string strString = address.Trim();
      string strReturn = null;
      string strTemp1 = null;
      string strTemp2 = null;

      if (strString.IndexOf("@") < 0)
      {
        return "";
      }

      strTemp1 = strString.Substring(0, strString.IndexOf("@"));
      strTemp2 = strString.Replace(strTemp1 + "@", "");

      switch (typeToReturn)
      {
        //Return fullname
        case PartType.Fullname:

          if (strTemp1.IndexOf(".") > 0)
          {
            //Firstname
            strTemp2 = FormatProperCase(strTemp1.Substring(0, strTemp1.IndexOf(".")));
            //Lastname
            strTemp1 = FormatProperCase(strTemp1.Substring(strTemp1.IndexOf(".") + 1, strTemp1.Length - strTemp1.IndexOf(".") - 1));

            strReturn = strTemp2 + " " + strTemp1;
          }
          else
          {
            strReturn = FormatProperCase(strTemp1);
          }


          return strReturn.Trim();
        case PartType.FirstName:

          if (strTemp1.IndexOf(".") > 0)
          {
            //Firstname
            strReturn = FormatProperCase(strTemp1.Substring(0, strTemp1.IndexOf(".")));
          }
          else
          {
            strReturn = "";
          }


          return strReturn.Trim();
        case PartType.LastName:

          if (strTemp1.IndexOf(".") > 0)
          {
            //Lastname
            strReturn = FormatProperCase(strTemp1.Substring(strTemp1.IndexOf(".") + 1, strTemp1.Length - strTemp1.IndexOf(".") - 1));
          }
          else
          {
            strReturn = FormatProperCase(strTemp1);
          }


          return strReturn.Trim();
        case PartType.CompanyName:

          if (strTemp2.IndexOf(".") > 0)
          {
            //Companyname
            strReturn = FormatProperCase(strTemp2.Substring(0, strTemp2.IndexOf(".")));
          }
          else
          {
            strReturn = FormatProperCase(strTemp2);
          }


          return strReturn.Trim();
        case PartType.Url:

          return "http://www." + strTemp2;
        default:

          return "";
      }

    }
    private string FormatProperCase(string stringToFormat)
    {
      var textInfo = Thread.CurrentThread.CurrentCulture.TextInfo;
      return textInfo.ToTitleCase(stringToFormat.ToLower());
    }

    /// <summary>
    /// Cleans up HTML and returns plain text
    /// </summary>
    /// <param name="source">The HTML to clean</param>
    public string CleanHtml(string source)
    {
      try
      {
        // Replace line breaks with space
        // because browsers inserts space
        var strResult = source.Replace("\r", " ");

        // Replace line breaks with space
        // because browsers inserts space
        strResult = strResult.Replace("\n", " ");

        // Remove step-formatting
        //strResult = strResult.Replace((char) 9, string.Empty);

        // Remove repeating spaces becuase browsers ignore them
        strResult = Regex.Replace(strResult, "( )+", " ");

        // Remove the header (prepare first by clearing attributes)
        strResult = Regex.Replace(strResult, "<( )*head([^>])*>", "<head>", RegexOptions.IgnoreCase);
        strResult = Regex.Replace(strResult, "(<( )*(/)( )*head( )*>)", "</head>", RegexOptions.IgnoreCase);
        strResult = Regex.Replace(strResult, "(<head>).*(</head>)", string.Empty, RegexOptions.IgnoreCase);

        // remove all scripts (prepare first by clearing attributes)
        strResult = Regex.Replace(strResult, "<( )*script([^>])*>", "<script>", RegexOptions.IgnoreCase);
        strResult = Regex.Replace(strResult, "(<( )*(/)( )*script( )*>)", "</script>", RegexOptions.IgnoreCase);
        strResult = Regex.Replace(strResult, "(<script>).*(</script>)", string.Empty, RegexOptions.IgnoreCase);

        // remove all styles (prepare first by clearing attributes)
        strResult = Regex.Replace(strResult, "<( )*style([^>])*>", "<style>", RegexOptions.IgnoreCase);
        strResult = Regex.Replace(strResult, "(<( )*(/)( )*style( )*>)", "</style>", RegexOptions.IgnoreCase);
        strResult = Regex.Replace(strResult, "(<style>).*(</style>)", string.Empty, RegexOptions.IgnoreCase);

        // insert tabs in spaces of <td> tags
        strResult = Regex.Replace(strResult, "<( )*td([^>])*>", ((char)9).ToString(), RegexOptions.IgnoreCase);

        // insert line breaks in places of <BR> and <LI> tags
        strResult = Regex.Replace(strResult, "<( )*br( )*>", "\r", RegexOptions.IgnoreCase);
        strResult = Regex.Replace(strResult, "<( )*li( )*>", "\r", RegexOptions.IgnoreCase);

        // insert line paragraphs (double line breaks) in place
        // if <P>, <DIV> and <TR> tags
        strResult = Regex.Replace(strResult, "<( )*div([^>])*>", "\r" + "\r", RegexOptions.IgnoreCase);
        strResult = Regex.Replace(strResult, "<( )*tr([^>])*>", "\r" + "\r", RegexOptions.IgnoreCase);
        strResult = Regex.Replace(strResult, "<( )*p([^>])*>", "\r" + "\r", RegexOptions.IgnoreCase);

        // Remove remaining tags like <a>, links, images,
        // comments etc - anything thats enclosed inside < >
        strResult = Regex.Replace(strResult, "<[^>]*>", string.Empty, RegexOptions.IgnoreCase);

        // replace special characters:
        strResult = Regex.Replace(strResult, " ", " ", RegexOptions.IgnoreCase);

        strResult = Regex.Replace(strResult, "&bull;", " * ", RegexOptions.IgnoreCase);
        strResult = Regex.Replace(strResult, "&lsaquo;", "<", RegexOptions.IgnoreCase);
        strResult = Regex.Replace(strResult, "&rsaquo;", ">", RegexOptions.IgnoreCase);
        strResult = Regex.Replace(strResult, "&trade;", "(tm)", RegexOptions.IgnoreCase);
        strResult = Regex.Replace(strResult, "&frasl;", "/", RegexOptions.IgnoreCase);
        strResult = Regex.Replace(strResult, "&lt;", "<", RegexOptions.IgnoreCase);
        strResult = Regex.Replace(strResult, "&gt;", ">", RegexOptions.IgnoreCase);
        strResult = Regex.Replace(strResult, "&copy;", "(c)", RegexOptions.IgnoreCase);
        strResult = Regex.Replace(strResult, "&reg;", "(r)", RegexOptions.IgnoreCase);
        strResult = Regex.Replace(strResult, "&(.{2,6});", string.Empty, RegexOptions.IgnoreCase);

        // make line breaking consistent
        strResult = strResult.Replace(((char)10).ToString(), "\r");

        // Remove extra line breaks and tabs:
        // replace over 2 breaks with 2 and over 4 tabs with 4. 
        // Prepare first to remove any whitespaces inbetween
        // the escaped characters and remove redundant tabs inbetween linebreaks
        strResult = Regex.Replace(strResult, "(" + "\r" + ")( )+(" + "\r" + ")", "" + "\r" + "" + "\r" + "", RegexOptions.IgnoreCase);
        strResult = Regex.Replace(strResult, "(" + (char)9 + ")( )+(" + (char)9 + ")", "" + (char)9 + "" + (char)9 + "", RegexOptions.IgnoreCase);
        strResult = Regex.Replace(strResult, "(" + (char)9 + ")( )+(" + "\r" + ")", "" + (char)9 + "" + "\r" + "", RegexOptions.IgnoreCase);
        strResult = Regex.Replace(strResult, "(" + "\r" + ")( )+(" + (char)9 + ")", "" + "\r" + "" + (char)9 + "", RegexOptions.IgnoreCase);

        // Remove redundant tabs
        strResult = Regex.Replace(strResult, "(" + "\r" + ")(" + (char)9 + ")+(" + "\r" + ")", "" + "\r" + "" + "\r" + "", RegexOptions.IgnoreCase);

        // Remove multible tabs followind a linebreak with just one tab
        strResult = Regex.Replace(strResult, "(" + "\r" + ")(" + (char)9 + ")+", "" + "\r" + "" + (char)9 + "", RegexOptions.IgnoreCase);

        strResult = strResult.Replace("\r" + "\r", "\r");
        strResult = strResult.Replace(((char)9).ToString() + ((char)9).ToString(), ((char)9).ToString());
        strResult = strResult.Replace("  ", " ");

        return strResult;

      }
      catch (Exception ex)
      {
        LogHelper.Log("Error clearing HTML.", ex, LogHelper.LogType.Warning);
        return source;
      }

    }

    /// <summary>
    /// Resolves names in an e-mail address.
    /// </summary>
    /// <param name="name">The e-mail address to resolve names for.</param>
    public NameStruct ResolveNames(string name)
    {
      var newName = new NameStruct();

      //Remove " and ' from e-mail
      name = name.Replace(((char)(34)).ToString(), "").Replace("'", "");

      if (name.IndexOf("<") > 0)
      {
        var strName = name.Substring(0, name.IndexOf("<") - 1).Trim();
        if (strName.Contains(" "))
        {
          newName.FirstName = strName.Split(" ".ToCharArray())[0].Trim();
          newName.LastName = strName.Split(" ".ToCharArray())[1].Trim();
        }
        else
        {
          newName.FirstName = strName;
        }
      }

      if (string.IsNullOrEmpty(newName.FirstName))
      {
        newName.FirstName = GetPartFromEMail(name, PartType.FirstName);
        newName.LastName = GetPartFromEMail(name, PartType.LastName);
      }
      return newName;
    }

    /// <summary>
    /// Sends e-mail using .NET Framework Smtp classes
    /// </summary>
    /// <param name="subject">The subject</param>
    /// <param name="body">The body</param>
    /// <param name="sendTo">Recipients</param>
    /// <param name="sendToBcc">Blind copies</param>
    /// <param name="sendFrom">From address</param>
    /// <param name="html">As HTML?</param>
    public void SendNetMail(string subject, string body, string sendTo, string sendToBcc, string sendFrom, bool html)
    {
      var objMailMsg = new MailMessage(sendFrom, sendTo);
      var objSmtp = new SmtpClient();

      objMailMsg.Body = body;
      objMailMsg.Subject = subject;
      objMailMsg.IsBodyHtml = html;

      if (!string.IsNullOrEmpty(sendToBcc))
      {
        objMailMsg.Bcc.Add(sendToBcc);
      }

      //Workaround for Google
      if (objSmtp.Host.Contains("gmail"))
      {
        objSmtp.EnableSsl = true;
      }

      objSmtp.Send(objMailMsg);

    }

    /// <summary>
    /// Sends e-mail using CRM
    /// </summary>
    /// <param name="subject">The subject</param>
    /// <param name="body">The body</param>
    /// <param name="sendTo">The to entity</param>
    /// <param name="sendFromId">The from entity ID</param>
    /// <param name="fromType">The from type</param>
    /// <param name="objCase">The case to relate to</param>
    /// <remarks></remarks>
    public void SendCrmMail(string subject, string body, vo.IncomingEntity sendTo, Guid sendFromId, string fromType, Incident objCase)
    {
      var email = new Email();
      var objFrom = new ActivityParty();
      var objTo = new ActivityParty();
      var objTrackingRequest = new GetTrackingTokenEmailRequest();
      var objSend = new SendEmailRequest();

      email.Subject = subject;
      email.Description = body;

      objFrom.PartyId = new EntityReference(fromType, sendFromId);

      objTo.PartyId = new EntityReference(sendTo.EntityLogicalName, sendTo.Id);

      email.From = new[] { objFrom };
      email.To = new[] { objTo };

      //Update the e-mail and set "regarding" to the case
      if (objCase != null)
      {
        email.RegardingObjectId = new EntityReference(Incident.EntityLogicalName, objCase.IncidentId.Value);
      }

      var objEMailId = Create(email);

      objTrackingRequest.Subject = subject;
      var objTrackingResponse = (GetTrackingTokenEmailResponse)_service.Execute(objTrackingRequest);

      objSend.EmailId = objEMailId;
      objSend.IssueSend = true;
      objSend.TrackingToken = objTrackingResponse.TrackingToken;

      _service.Execute(objSend);
    }

    public void SendCrmMail(string subject, string body, string fromEntityLogicalName, Guid fromId, string toEntityLogicalName, Guid toId, Guid? bccContactId, Incident incident)
    {
      // Create the 'From:' activity party for the email
      var fromParty = new ActivityParty { PartyId = new EntityReference(fromEntityLogicalName, fromId) };

      // Create the 'To:' activity party for the email
      var toParty = new ActivityParty { PartyId = new EntityReference(toEntityLogicalName, toId) };

      // Create an e-mail message.
      var email = new Email
      {
        To = new[] { toParty },
        From = new[] { fromParty },
        Subject = subject,
        Description = body,
        DirectionCode = true,
        OwnerId = incident.OwnerId
      };

      // Create the 'Bcc:' activity party for the email, if available
      if (bccContactId != null)
      {
        var bccParty = new ActivityParty { PartyId = new EntityReference(Contact.EntityLogicalName, bccContactId.Value) };
        email.Bcc = new[] { bccParty };
      }

      var emailId = _service.Create(email);
      email.Id = emailId;

      //Associate the email with the case
      AssociateEmailWithCase(email, incident);

      // Use the SendEmail message to send an e-mail message.
      var sendEmailreq = new SendEmailRequest
      {
        EmailId = emailId,
        TrackingToken = "",
        IssueSend = true
      };

      var sendEmailresp = (SendEmailResponse)_service.Execute(sendEmailreq);

    }

    /// <summary>
    /// Routes an e-mail from one queue to another
    /// </summary>
    /// <param name="email">The e-mail to route</param>
    /// <param name="sourceQueueId">Sourec queue</param>
    /// <param name="destinationQueueId">Target queue</param>
    public void RouteEMail(Email email, Guid sourceQueueId, Guid destinationQueueId)
    {
      // Move a record from a source queue to a destination queue
      // by using the AddToQueue request message.
      var routeRequest = new AddToQueueRequest
      {
        SourceQueueId = sourceQueueId,
        Target = new EntityReference(Email.EntityLogicalName, email.Id),
        DestinationQueueId = destinationQueueId
      };

      // Execute the Request
      _service.Execute(routeRequest);
    }

  }
}
